using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;

static partial class Program
{


    static void Main(string[] args) {
        // todo - write to log on app domain torn?
        Helpers.Log("Starting " + Helpers.PID + "...");
        try {

            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            // any other instances running ?
            var others = Helpers.CheckOtherInstancesActive();
            int port;

            const string port_dat = "port.dat";
            bool must_write_file = false;
            
            if(File.Exists(port_dat)) {

                port = int.Parse(File.ReadAllText(port_dat));

            }
            else if (!others) {
                port = 15128;
                must_write_file = true;
                
            }
            else {
                // todo - static page OR check via API
                throw new HerculesException("Unable to find previous instance port number");
            }
            
            
            if(!others && !Helpers.CheckIsPortAvailable(port)) {
                port = Helpers.GetTcpPort();
                must_write_file = true;
                
            }
            
            if(must_write_file) {
                Debug.Assert(!others);
                File.WriteAllText(port_dat, port + "");
                
            }

            var prefix = Helpers.GetUrl(port);
            var url = prefix + page_start;

            if (others) {
                Process.Start(url);

            }
            else {
                Initialize();

                RequestArrived += (_1, _args) => {
                    Helpers.Log(_args.Request.RawUrl);

                };

                using (HttpListener listener = new HttpListener()) {

                    listener.Prefixes.Add(prefix);
                    listener.Start();
                    Helpers.Log("Listening on " + port + "...");
                    File.WriteAllText(port_dat, port + "");

                    Process.Start(url);

                    MainLoop(listener);

                }


            }


        }
        catch (Exception ex) {
            OnExceptionUnhandled(ex);
        }
        finally {
            Helpers.Log("Closing " + Helpers.PID + "...");
        }
    }




    static void Initialize() {

        Directory.CreateDirectory(Program.dir_foods);
        Directory.CreateDirectory(Program.dir_exercises);
        Directory.CreateDirectory(Program.dir_profiles);
        
    }






    static event EventHandler<ExceptionEventArgs> ExceptionUnhandled;
    static event EventHandler<RequestArrivedEventArgs> RequestArrived;

    static void OnRequestArrived(HttpListenerRequest hlr) {
        var handler = RequestArrived;
        if (handler != null) {
            handler(null, new RequestArrivedEventArgs(hlr));
        }
    }
    static void OnExceptionUnhandled(Exception ex) {

        Helpers.Log(ex);

        var handler = ExceptionUnhandled;
        if (handler != null) {
            handler(null, new ExceptionEventArgs(ex));
        }
    }




    static void MainLoop(HttpListener listener) {

        while (true) {

            // get request
            HttpListenerContext context = listener.GetContext();

            // raise event
            OnRequestArrived(context.Request);

            HttpListenerRequest request = context.Request;
            HttpListenerResponse response = context.Response;


            // inspect request
            IReaction reaction;

            try {
                reaction = ReactionFactory.GetReaction(request);
            }
            catch (Exception ex) {


                OnExceptionUnhandled(ex);

                reaction = ReactionFactory.GetReaction(request, ex);

            }

            // do whatever housekeeping needs to be performed
            reaction.PerformOperations();

            // setup HTTP response
            response.StatusCode = (int)reaction.HttpStatusCode;

            var content = reaction.GetContent();
            var encoding = Encoding.UTF8; 

            // response.ContentLength64 = encoding.GetByteCount(content);

            if (reaction.Redirect) {

                response.RedirectLocation = reaction.RedirectLocation;

            }

   
            // write content

            var s = response.OutputStream;
            using (var writer = new StreamWriter(s, encoding)) {
                writer.Write(content);
            }



            // exit loop
            if (reaction == ReactionFactory.Exit) {
                break;
            }

        }

        listener.Stop();

    }

}




